/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.odb.bzk3.midp;

import java.io.InputStream;
import java.util.Vector;

import br.odb.bzk3.geometry.Directions;
import br.odb.bzk3.geometry.ObjMesh;
import br.odb.libscene.Actor;
import br.odb.libscene.Sector;
import br.odb.utils.math.Vec2;
import br.odb.utils.math.Vec3;

/**
 * 
 * @author daniel
 */
public class EngineView implements Runnable {

	Vector people;
	World world;
	byte currentDirection;
	M3GRenderer render;
	ObjMesh mesh;
	public float x;
	public float y;
	public float z;
	public int angle;
	private Vec2[] translations;
	public GameActor cameraActor;
	private String mapToLoad;
	byte frame = 0;

	public EngineView() {
		people = new Vector();
		cameraActor = null;

		render = new M3GRenderer();
		translations = new Vec2[17];
		angle = 0;
		translations[0] = new Vec2(0, 1);
		translations[1] = new Vec2(0.38f, 0.93f);
		translations[2] = new Vec2(0.7f, 0.7f);
		translations[3] = new Vec2(0.7f, 0.38f);
		translations[4] = new Vec2(1, 0);
		translations[5] = new Vec2(0.93f, -0.38f);
		translations[6] = new Vec2(0.7f, -0.7f);
		translations[7] = new Vec2(0.38f, -0.93f);
		translations[8] = new Vec2(0, -1);
		translations[9] = new Vec2(-0.38f, -0.93f);
		translations[10] = new Vec2(-0.7f, -0.7f);
		translations[11] = new Vec2(-0.93f, -0.38f);
		translations[12] = new Vec2(-1, 0);
		translations[13] = new Vec2(-0.93f, 0.38f);
		translations[14] = new Vec2(-0.7f, 0.7f);
		translations[15] = new Vec2(-0.38f, 0.93f);
		translations[16] = new Vec2(0, 1);

		x = 0;
		y = 0;
		z = 0;

		try {
			InputStream is = file("riffle.bin");
			int entries = is.read();
			byte[] bytes = new byte[10];
			Triangle t;
			int range = Byte.MAX_VALUE - Byte.MIN_VALUE;
			for (int c = 0; c < entries; ++c) {

				is.read(bytes);

				t = new Triangle();

				t.a = (bytes[0] + (-Byte.MIN_VALUE));
				t.r = (bytes[1] + (-Byte.MIN_VALUE));
				t.g = (bytes[2] + (-Byte.MIN_VALUE));
				t.b = (bytes[3] + (-Byte.MIN_VALUE));

				t.x0 = (((bytes[4] + (-Byte.MIN_VALUE)) * render.getWidth()) / range);
				t.y0 = (((bytes[5] + (-Byte.MIN_VALUE)) * render.getHeight()) / range);
				t.x1 = (((bytes[6] + (-Byte.MIN_VALUE)) * render.getWidth()) / range);
				t.y1 = (((bytes[7] + (-Byte.MIN_VALUE)) * render.getHeight()) / range);
				t.x2 = (((bytes[8] + (-Byte.MIN_VALUE)) * render.getWidth()) / range);
				t.y2 = (((bytes[9] + (-Byte.MIN_VALUE)) * render.getHeight()) / range);

				render.shapes.addElement(t);

				t.flatten();
				t.init();
			}
		} catch (Exception e) {

		}
	}

	public EngineView(int selectedIndex) {
		this();
		Vector mapNames = new Vector();
		// mapNames.addElement("level_1_1.will");
		mapNames.addElement("prison.level");
		mapNames.addElement("level1.level");
		mapNames.addElement("level0.level");
		mapNames.addElement("level_1_4.will");

		mapNames.addElement("level_2_1.will");
		mapNames.addElement("level_2_2.will");
		mapNames.addElement("level_2_3.will");
		mapNames.addElement("level_2_4.will");
		mapNames.addElement("level_2_5.will");
		mapToLoad = (String) mapNames.elementAt(selectedIndex);
	}

	public M3GRenderer GetRender() {
		return render;
	}

	public void Pause() {
	}

	private int clamp(int angle) {
		int quadrant = angle;
		while (quadrant < 0) {
			quadrant += 16;
		}
		quadrant = (quadrant) % 16;
		return quadrant;
	}

	public void run() {

		internalize();

		for (int c = 1; c < world.getTotalSectors(); c++) {
			world.getSector(c).setVisible(false);
			// world.getSector(c).iCache = null;
		}
		System.gc();
//		for (int e = 1; e < people.size(); e++) {
//			mesh = new ObjMesh();
//			mesh.origin.iX = ((Actor) people.elementAt(e)).iStartPoint.iX;
//			mesh.origin.iY = ((Actor) people.elementAt(e)).iStartPoint.iY;
//			mesh.origin.iZ = ((Actor) people.elementAt(e)).iStartPoint.iZ;
//			mesh.internalize(file("ghost.obj"));
//
//			for (int d = 0; d < mesh.meshes.size(); d++) {
//				render.addFace((Mesh) mesh.meshes.elementAt(d));
//			}
//		}
		render.background.setColor(0);

		while (true) {
			HandleKeys();
			for (int c = 0; c < people.size(); c++) {
				((Actor) people.elementAt(c)).tick();
				Lit((Actor) people.elementAt(c));
			}

			currentDirection = Directions
					.getDirection(((clamp(angle) / 16.0f) * 360.0f));
			// System.out.println("direction: " + currentDirection +
			// " clamped: " + clamp( angle ) );

			percorre(this.cameraActor.getCurrentSector());
			render.repaint();
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			for (int c = 1; c < world.getTotalSectors(); c++) {
				world.getSector(c).reset();
			}
			// System.out.println( "frame: " + frame );
			++frame;

			if (frame == Byte.MAX_VALUE - 1)
				frame = Byte.MIN_VALUE + 1;
		}
	}

	public void percorre(int sec) {
		if (sec >= world.getTotalSectors())
			return;

		Sector s = world.getSector(sec);

		if (s.lastFrame == frame)
			return;

		s.lastFrame = frame;

		// if (s.visited) return;
		// s.visited=true;

		// if ( !s.isVisible() )
		for (int d = 0; d < 6; d++) {
			if (s.GetLink(d) == 0) {
				render.AddFaceFromSector(s, d);

			}
		}

		s.setVisible(true);

		if (s.GetLink(currentDirection) != 0)
			percorre(s.GetLink(currentDirection));

		if (s.GetLink(Directions.getLeft(currentDirection)) != 0)
			percorre(s.GetLink(Directions.getLeft(currentDirection)));

		if (s.GetLink(Directions.getRight(currentDirection)) != 0)
			percorre(s.GetLink(Directions.getRight(currentDirection)));

		if (s.GetLink(Directions.FLOOR) != 0)
			percorre(s.GetLink(Directions.FLOOR));

		if (s.GetLink(Directions.CEILING) != 0)
			percorre(s.GetLink(Directions.CEILING));

	}

	private void HandleKeys() {
		if (this.GetRender().Keymaps[4] == 1) {
			y += 0.01;

		}
		if (this.GetRender().Keymaps[5] == 1) {
			y -= 0.01;
		}

		if (this.GetRender().Keymaps[0] == 1) {
			x -= 0.5 * translations[clamp(angle)].getX();
			z -= 0.5 * translations[clamp(angle)].getY();
		}

		if (this.GetRender().Keymaps[2] == 1) {
			x += 0.5 * translations[clamp(angle)].getX();
			z += 0.5 * translations[clamp(angle)].getY();
		}

		if (this.GetRender().Keymaps[1] == 1) {
			{
				try {
					angle -= 1;
					cameraActor.setAngleXZ( M3GRenderer.GetDirection(angle) * 22.5f );
					render.setAngle((22.5f * angle));
					Thread.sleep(200);
				} catch (InterruptedException ex) {
					ex.printStackTrace();
				}

			}
		}
		if (this.GetRender().Keymaps[3] == 1) {
			{
				try {
					angle += 1;
					cameraActor.setAngleXZ( M3GRenderer.GetDirection(angle) * 22.5f );
					render.setAngle( (22.5f * angle));
					Thread.sleep(200);
				} catch (InterruptedException ex) {
					ex.printStackTrace();
				}

			}
		}
		Vec3 v = new Vec3();
		v.setX( v.getX() + x );
		v.setY( v.getY() + y );
		v.setZ( v.getZ() + z );		
		cameraActor.setPosition( v );

		cameraActor.setAngleXZ( cameraActor.getAngleXZ() + angle );

		Sector sector;

		sector = world.getSector(cameraActor.getCurrentSector());

		if (sector.locateEscapePlane(cameraActor.getPosition()) != 100) {
			System.out.println("link:"
					+ sector.locateEscapePlane(cameraActor.getPosition())
					+ " sector:" + cameraActor.getCurrentSector() + " pos:"
					+ cameraActor.getPosition().toString());

			if (sector.GetLink(sector
					.locateEscapePlane(cameraActor.getPosition())) != 0) {
				cameraActor.Sector = sector.GetLink(sector
						.locateEscapePlane(cameraActor.getPosition()));
				System.out.println("->Sector:" + cameraActor.getCurrentSector());
			} else {
				
				Vec3 v = new Vec3();
				v.setX( cameraActor.getPosition().getX() - x );
				v.setY( cameraActor.getPosition().getY() - y );
				v.setZ( cameraActor.getPosition().getZ() - z );
				cameraActor.setPosition( v );
				x = 0;
				y = 0;
				z = 0;
			}
		}
		render.Translate(x, y, z);
		x = 0;
		y = 0;
		z = 0;
	}

	public void PlaceActor(int asector, Actor actor, boolean camera) {

		Sector sector = world.getSector(asector);
		Vec3 vec = new Vec3();

		vec.iX = (sector.iStartPoint.iX + sector.iDimensions.iX / 2);
		vec.iY = (sector.iStartPoint.iY + sector.iDimensions.iY / 2);
		vec.iZ = (sector.iStartPoint.iZ + sector.iDimensions.iZ / 2);

		actor.iStartPoint.iX = (vec.iX);
		actor.iStartPoint.iY = (vec.iY);
		actor.iStartPoint.iZ = (vec.iZ);

		if (camera) {
			render.iOrigin.iX = (vec.iX);
			render.iOrigin.iY = (vec.iY);
			render.iOrigin.iZ = (vec.iZ);
		}
		actor.Sector = asector;
		render.Reset();
	}

	private InputStream file(String mapFile) {
		InputStream is = getClass().getResourceAsStream(
				"/br/odb/bzk3/life/levels/" + mapFile);
		return is;
	}

	void internalize() {
		world = new World(file(mapToLoad), people);
		System.gc();
		render.Reset();
		render.setAngle( (22.5f * angle));
		cameraActor = (Actor) people.elementAt(0);
		PlaceActor(cameraActor.Sector, cameraActor, true);
	}

//	private void LitSector(int sector, int candelas) {
//		Sector sec = world.getSector(sector);
//
//		float factor = (candelas) - 5.0f;
//
//		sec.candelas += factor;
//
//		if (sec.candelas >= 255) {
//			sec.candelas = 254;
//		}
//
//		factor -= sec.VisibleFaces * 5.0f;
//
//		if (factor < 8) {
//			return;
//		}
//
//		for (int c = 0; c < 6; c++) {
//			if (sec.GetLink(c) != 0) {
//				LitSector(sec.GetLink(c), (int) factor);
//			}
//		}
//	}

	private void Lit(Actor actor) {
		if (actor.candelas > 0) {
			LitSector(actor.Sector, actor.candelas);
		}
	}
}
